package nachos.userprog;

import nachos.machine.*;
import nachos.threads.*;
import nachos.userprog.*;

import java.io.EOFException;
import java.io.File;
import java.util.Vector;

/**
 * Encapsulates the state of a user process that is not contained in its user
 * thread (or threads). This includes its address translation state, a file
 * table, and information about the program being executed.
 * 
 * <p>
 * This class is extended by other classes to support additional functionality
 * (such as additional syscalls).
 * 
 * @see nachos.vm.VMProcess
 * @see nachos.network.NetProcess
 */
public class UserProcess {
	/**
	 * Allocate a new process.
	 */
	public UserProcess() {
		int numPhysPages = Machine.processor().getNumPhysPages();
		pageTable = new TranslationEntry[numPhysPages];
		for (int i = 0; i < numPhysPages; i++)
			pageTable[i] = new TranslationEntry(i, i, true, false, false, false);
		files = new OpenFile[maxOpenFiles];
		filePositions = new int[maxOpenFiles];
		files[0] = UserKernel.console.openForReading();
		files[1] = UserKernel.console.openForWriting();

		filePositions[0] = 0;
		filePositions[1] = 0;

		children = new Vector<UserProcess>();
		waitForDeath = new Semaphore(0);
	}

	/**
	 * Allocate and return a new process of the correct class. The class name is
	 * specified by the <tt>nachos.conf</tt> key
	 * <tt>Kernel.processClassName</tt>.
	 * 
	 * @return a new process of the correct class.
	 */
	public static UserProcess newUserProcess() {
		return (UserProcess) Lib.constructObject(Machine.getProcessClassName());
	}

	/**
	 * Execute the specified program with the specified arguments. Attempts to
	 * load the program, and then forks a thread to run it.
	 * 
	 * @param name
	 *            the name of the file containing the executable.
	 * @param args
	 *            the arguments to pass to the executable.
	 * @return <tt>true</tt> if the program was successfully executed.
	 */
	public boolean execute(String name, String[] args) {
		if (!load(name, args))
			return false;

		(thisThread = new UThread(this)).setName(name).fork();

		numRunningProcesses++;

		return true;
	}

	/**
	 * Save the state of this process in preparation for a context switch.
	 * Called by <tt>UThread.saveState()</tt>.
	 */
	public void saveState() {
	}

	/**
	 * Restore the state of this process after a context switch. Called by
	 * <tt>UThread.restoreState()</tt>.
	 */
	public void restoreState() {
		Machine.processor().setPageTable(pageTable);
	}

	/**
	 * Read a null-terminated string from this process's virtual memory. Read at
	 * most <tt>maxLength + 1</tt> bytes from the specified address, search for
	 * the null terminator, and convert it to a <tt>java.lang.String</tt>,
	 * without including the null terminator. If no null terminator is found,
	 * returns <tt>null</tt>.
	 * 
	 * @param vaddr
	 *            the starting virtual address of the null-terminated string.
	 * @param maxLength
	 *            the maximum number of characters in the string, not including
	 *            the null terminator.
	 * @return the string read, or <tt>null</tt> if no null terminator was
	 *         found.
	 */
	public String readVirtualMemoryString(int vaddr, int maxLength) {
		Lib.assertTrue(maxLength >= 0);

		byte[] bytes = new byte[maxLength + 1];

		int bytesRead = readVirtualMemory(vaddr, bytes);

		for (int length = 0; length < bytesRead; length++) {
			if (bytes[length] == 0)
				return new String(bytes, 0, length);
		}

		return null;
	}

	/**
	 * Transfer data from this process's virtual memory to all of the specified
	 * array. Same as <tt>readVirtualMemory(vaddr, data, 0, data.length)</tt>.
	 * 
	 * @param vaddr
	 *            the first byte of virtual memory to read.
	 * @param data
	 *            the array where the data will be stored.
	 * @return the number of bytes successfully transferred.
	 */
	public int readVirtualMemory(int vaddr, byte[] data) {
		return readVirtualMemory(vaddr, data, 0, data.length);
	}

	/**
	 * Transfer data from this process's virtual memory to the specified array.
	 * This method handles address translation details. This method must
	 * <i>not</i> destroy the current process if an error occurs, but instead
	 * should return the number of bytes successfully copied (or zero if no data
	 * could be copied).
	 * 
	 * @param vaddr
	 *            the first byte of virtual memory to read.
	 * @param data
	 *            the array where the data will be stored.
	 * @param offset
	 *            the first byte to write in the array.
	 * @param length
	 *            the number of bytes to transfer from virtual memory to the
	 *            array.
	 * @return the number of bytes successfully transferred.
	 */
	public int readVirtualMemory(int vaddr, byte[] data, int offset, int length) {
		Lib.assertTrue(offset >= 0 && length >= 0
				&& offset + length <= data.length);

		// local Variables
		int vPageNum;
		int pPageNum;
		int pageOffset;
		int physAddr;
		int endOfPage; // The last physical address of this page
		int bytesWritten;

		byte[] memory = Machine.processor().getMemory();
		bytesWritten = 0;

		while (length > 0) {
			// Translate virtual address to physical address
			vPageNum = Processor.pageFromAddress(vaddr);
			// Get the page offset from the vaddr
			pageOffset = Processor.offsetFromAddress(vaddr);

			TranslationEntry PTE = null; // Page Table Entry
			for (TranslationEntry te : pageTable) {
				if (te.vpn == vPageNum) {
					PTE = te;
					break;
				}
			}
			// Make sure the page is in the table
			if (PTE == null)
				return bytesWritten;
			// Get the physical page number from the table entry
			pPageNum = PTE.ppn;
			// Get the physical address from the physical page number
			physAddr = Processor.makeAddress(pPageNum, pageOffset);

			int amount = Math.min(length, Machine.processor().pageSize
					- pageOffset);
			System.arraycopy(memory, physAddr, data, offset, amount);

			offset += amount;
			length -= amount;
			vaddr += amount;
			bytesWritten += amount;
		}

		return bytesWritten;
	}

	/**
	 * Transfer all data from the specified array to this process's virtual
	 * memory. Same as <tt>writeVirtualMemory(vaddr, data, 0, data.length)</tt>.
	 * 
	 * @param vaddr
	 *            the first byte of virtual memory to write.
	 * @param data
	 *            the array containing the data to transfer.
	 * @return the number of bytes successfully transferred.
	 */
	public int writeVirtualMemory(int vaddr, byte[] data) {
		return writeVirtualMemory(vaddr, data, 0, data.length);
	}

	/**
	 * Transfer data from the specified array to this process's virtual memory.
	 * This method handles address translation details. This method must
	 * <i>not</i> destroy the current process if an error occurs, but instead
	 * should return the number of bytes successfully copied (or zero if no data
	 * could be copied).
	 * 
	 * @param vaddr
	 *            the first byte of virtual memory to write.
	 * @param data
	 *            the array containing the data to transfer.
	 * @param offset
	 *            the first byte to transfer from the array.
	 * @param length
	 *            the number of bytes to transfer from the array to virtual
	 *            memory.
	 * @return the number of bytes successfully transferred.
	 */
	public int writeVirtualMemory(int vaddr, byte[] data, int offset, int length) {
		Lib.assertTrue(offset >= 0 && length >= 0
				&& offset + length <= data.length);

		// local Variables
		int vPageNum;
		int pPageNum;
		int pageOffset;
		int physAddr;
		int endOfPage; // The last physical address of this page
		int bytesWritten;

		byte[] memory = Machine.processor().getMemory();
		bytesWritten = 0;

		while (length > 0) {
			// Translate virtual address to physical address
			vPageNum = Processor.pageFromAddress(vaddr);
			// Get the page offset from the vaddr
			pageOffset = Processor.offsetFromAddress(vaddr);

			TranslationEntry PTE = null; // Page Table Entry
			for (TranslationEntry te : pageTable) {
				if (te.vpn == vPageNum) {
					if (!te.readOnly)
						PTE = te;
					break;
				}
			}
			// Make sure the page is in the table
			if (PTE == null)
				return bytesWritten;
			// Get the physical page number from the table entry
			pPageNum = PTE.ppn;
			// Get the physical address from the physical page number
			physAddr = Processor.makeAddress(pPageNum, pageOffset);

			int amount = Math.min(length, Machine.processor().pageSize
					- pageOffset);
			System.arraycopy(data, offset, memory, physAddr, amount);

			offset += amount;
			length -= amount;
			vaddr += amount;
			bytesWritten += amount;
		}

		return bytesWritten;
	}

	/**
	 * Load the executable with the specified name into this process, and
	 * prepare to pass it the specified arguments. Opens the executable, reads
	 * its header information, and copies sections and arguments into this
	 * process's virtual memory.
	 * 
	 * @param name
	 *            the name of the file containing the executable.
	 * @param args
	 *            the arguments to pass to the executable.
	 * @return <tt>true</tt> if the executable was successfully loaded.
	 */
	private boolean load(String name, String[] args) {
		Lib.debug(dbgProcess, "UserProcess.load(\"" + name + "\")");

		OpenFile executable = ThreadedKernel.fileSystem.open(name, false);
		if (executable == null) {
			Lib.debug(dbgProcess, "\topen failed");
			return false;
		}

		try {
			coff = new Coff(executable);
		} catch (EOFException e) {
			executable.close();
			Lib.debug(dbgProcess, "\tcoff load failed");
			return false;
		}

		// make sure the sections are contiguous and start at page 0
		numPages = 0;
		for (int s = 0; s < coff.getNumSections(); s++) {
			CoffSection section = coff.getSection(s);
			if (section.getFirstVPN() != numPages) {
				coff.close();
				Lib.debug(dbgProcess, "\tfragmented executable");
				return false;
			}
			numPages += section.getLength();
		}

		// make sure the argv array will fit in one page
		byte[][] argv = new byte[args.length][];
		int argsSize = 0;
		for (int i = 0; i < args.length; i++) {
			argv[i] = args[i].getBytes();
			// 4 bytes for argv[] pointer; then string plus one for null byte
			argsSize += 4 + argv[i].length + 1;
		}
		if (argsSize > pageSize) {
			coff.close();
			Lib.debug(dbgProcess, "\targuments too long");
			return false;
		}

		// program counter initially points at the program entry point
		initialPC = coff.getEntryPoint();

		// next comes the stack; stack pointer initially points to top of it
		numPages += stackPages;
		initialSP = numPages * pageSize;

		// and finally reserve 1 page for arguments
		numPages++;

		if (!loadSections())
			return false;

		// store arguments in last page
		int entryOffset = (numPages - 1) * pageSize;
		int stringOffset = entryOffset + args.length * 4;

		this.argc = args.length;
		this.argv = entryOffset;

		for (int i = 0; i < argv.length; i++) {
			byte[] stringOffsetBytes = Lib.bytesFromInt(stringOffset);
			Lib.assertTrue(writeVirtualMemory(entryOffset, stringOffsetBytes) == 4);
			entryOffset += 4;
			Lib.assertTrue(writeVirtualMemory(stringOffset, argv[i]) == argv[i].length);
			stringOffset += argv[i].length;
			Lib.assertTrue(writeVirtualMemory(stringOffset, new byte[] { 0 }) == 1);
			stringOffset += 1;
		}

		return true;
	}

	/**
	 * Allocates memory for this process, and loads the COFF sections into
	 * memory. If this returns successfully, the process will definitely be run
	 * (this is the last step in process initialization that can fail).
	 * 
	 * @return <tt>true</tt> if the sections were successfully loaded.
	 */
	protected boolean loadSections() {
		if (numPages > Machine.processor().getNumPhysPages()) {
			coff.close();
			Lib.debug(dbgProcess, "\tinsufficient physical memory");
			return false;
		}

		// Get list of physical pages to make page table
		physicalPages = UserKernel.requestPages(numPages);

		// Is there enough free physical memory?
		// TODO: Possibly change what happens in this case
		if (physicalPages[0] == -1) {
			coff.close();
			Lib.debug(dbgProcess, "\tinsufficient free physical memory");
			return false;
		}
		pageTable = new TranslationEntry[numPages];
		int physIndex = 0;

		// load sections
		for (int s = 0; s < coff.getNumSections(); s++) {
			CoffSection section = coff.getSection(s);

			Lib.debug(dbgProcess, "\tinitializing " + section.getName()
					+ " section (" + section.getLength() + " pages)");

			for (int i = 0; i < section.getLength(); i++) {
				int vpn = section.getFirstVPN() + i;

				// Store translation entry into page table
				pageTable[physIndex] = new TranslationEntry(vpn,
						physicalPages[physIndex], true, section.isReadOnly(),
						false, false);

				// Use proper physical address
				section.loadPage(i, physicalPages[physIndex]);

				// increment our list of physical pages
				physIndex++;
			}
		}

		//Allocate stack
		while (physIndex < physicalPages.length)
		{
			pageTable[physIndex] = new TranslationEntry(physIndex,
					physicalPages[physIndex], true, false,false, false);
			physIndex++;
		}
		return true;
	}

	/**
	 * Release any resources allocated by <tt>loadSections()</tt>.
	 */
	protected void unloadSections() {
		// YAY Java garbage collector! Clear the page table
		pageTable = null;
		UserKernel.releasePages(physicalPages);

	}

	/**
	 * Initialize the processor's registers in preparation for running the
	 * program loaded into this process. Set the PC register to point at the
	 * start function, set the stack pointer register to point at the top of the
	 * stack, set the A0 and A1 registers to argc and argv, respectively, and
	 * initialize all other registers to 0.
	 */
	public void initRegisters() {
		Processor processor = Machine.processor();

		// by default, everything's 0
		for (int i = 0; i < processor.numUserRegisters; i++)
			processor.writeRegister(i, 0);

		// initialize PC and SP according
		processor.writeRegister(Processor.regPC, initialPC);
		processor.writeRegister(Processor.regSP, initialSP);

		// initialize the first two argument registers to argc and argv
		processor.writeRegister(Processor.regA0, argc);
		processor.writeRegister(Processor.regA1, argv);
	}

	/**
	 * Handle the halt() system call.
	 */
	private int handleHalt() {

		if (PID != 0)
			return 0;

		Machine.halt();

		Lib.assertNotReached("Machine.halt() did not halt machine!");
		return 0;
	}

	/**
	 * Handle the creat() system call.
	 */
	private int handleCreate(int namePtr) {

		int fileId = getFileId();

		// If maximum number of files are open, return error.
		if (fileId == -1)
			return fileId;

		String filename = readVirtualMemoryString(namePtr, maxStringLength);
		// If file has been unlinked it can not be opened.
		if (unlinkedFiles.contains(filename))
			return -1;

		OpenFile file = ThreadedKernel.fileSystem.open(filename, true);

		if (file != null) {
			files[fileId] = file;
			filePositions[fileId] = 0;
			allFiles.add(filename);
		} else {
			return -1; // file was not able to be created.
		}

		return fileId;
	}

	/**
	 * Handle the open() syscall.
	 */
	private int handleOpen(int namePtr) {
		int fileId = getFileId();

		// If maximum number of files are open, return error.
		if (fileId == -1)
			return fileId;

		String filename = readVirtualMemoryString(namePtr, maxStringLength);
		// If a file has been unlinked it can't be opened.
		if (unlinkedFiles.contains(filename))
			return -1;

		OpenFile file = ThreadedKernel.fileSystem.open(filename, false);

		if (file != null) {
			files[fileId] = file;
			filePositions[fileId] = 0;
			allFiles.add(filename);
		} else {
			return -1; // file was not able to be opened.
		}

		return fileId;
	}

	/**
	 * Handles the close() system call.
	 */
	private int handleClose(int fileId) {
		if ((fileId < 0) || (fileId >= files.length))
			return -1;

		OpenFile file = files[fileId];
		if (file == null)
			return -1;
		String name = file.getName();
		allFiles.remove(name);
		if (unlinkedFiles.contains(name) && !allFiles.contains(name)) {
			ThreadedKernel.fileSystem.remove(name);
			unlinkedFiles.remove(name);
		}
		files[fileId] = null;
		filePositions[fileId] = 0;
		file.close();
		return 0;
	}

	/**
	 * Handle the read() syscall.
	 */
	private int handleRead(int fileID, int buffer, int buffLength) {
		OpenFile file;

		if (files[fileID] != null)
			file = files[fileID];
		else
			// File DNE or is not open
			return -1;

		byte[] ourBuff = new byte[fileBufferSize];
		int totalBytesRead = 0;

		for (int i = 0; i < buffLength; i += fileBufferSize) {
			int bytesRead;
			if (file.getFileSystem() == null && file.getName().equals("SynchConsole")) {
				// Can't read from stdout
				if (fileID == 1)
					return -1;
				bytesRead = file.read(ourBuff,0,
						Math.min(fileBufferSize, buffLength - i));
			}
			else
				bytesRead = file.read(filePositions[fileID] + i, ourBuff, 0,
						Math.min(fileBufferSize, buffLength - i));
			if (bytesRead == -1)
				return -1;
			writeVirtualMemory(buffer + i, ourBuff, 0, bytesRead);

			totalBytesRead += bytesRead;
			// If EOF reached.
			if (bytesRead != Math.min(fileBufferSize, buffLength - i))
				break;
		}

		filePositions[fileID] += totalBytesRead;

		return totalBytesRead;
	}

	/**
	 * Handle the write() syscall.
	 */
	private int handleWrite(int fileID, int buffer, int buffLength) {
		OpenFile file;

		if (files[fileID] != null)
			file = files[fileID];
		else
			// File DNE or is not open
			return -1;

		byte[] ourBuff = new byte[fileBufferSize];
		int totalBytesWritten = 0;

		for (int i = 0; i < buffLength; i += fileBufferSize) {
			int bytesWritten = readVirtualMemory(buffer + i, ourBuff, 0,
					Math.min(fileBufferSize, buffLength - i));
			if (bytesWritten == -1)
				return bytesWritten;
			
			if (file.getFileSystem() == null && file.getName().equals("SynchConsole")) {
				// Can't write to stdin
				if (fileID == 0)
					return -1;
				file.write(ourBuff, 0, bytesWritten);
			}
			else
				file.write(filePositions[fileID] + i, ourBuff, 0, bytesWritten);

			totalBytesWritten += bytesWritten;
			// Ran out of room on disk or similar failure.
			if (bytesWritten != Math.min(fileBufferSize, buffLength - i))
				return -1;
		}

		filePositions[fileID] += totalBytesWritten;

		return totalBytesWritten;
	}

	/**
	 * Handles the unlink() syscall.
	 */
	private int handleUnlink(int namePtr) {
		String filename = readVirtualMemoryString(namePtr, maxStringLength);
		// If this file has already been unlinked, return error.
		if (unlinkedFiles.contains(filename))
			return -1;
		if (allFiles.contains(filename)) {
			unlinkedFiles.add(filename);
			return 0;
		}
		boolean success = ThreadedKernel.fileSystem.remove(filename);
		if (success)
			return 0;
		else
			return -1;
	}

	/**
	 * Handles the exec() syscall.
	 */
	private int handleExec(int namePtr, int argc, int argv) {
		UserProcess process = UserProcess.newUserProcess();
		process.PID = UserProcess.nextPID;
		children.add(process);
		UserProcess.nextPID++;
		
		String filename = readVirtualMemoryString(namePtr,maxStringLength);
		String[] args = new String[argc];
		// A byte array containing the pointer as 4 bytes
		byte[] pointArry = new byte[4];
		int pointer;
		for (int i = 0; i < argc; i++)
		{
			readVirtualMemory(argv, pointArry);
			pointer = Lib.bytesToInt(pointArry, 0);
			args[i] = readVirtualMemoryString(pointer, maxStringLength);
			argv += 4;
		}
		if(!process.execute(filename, args))
			return -1;
		return process.PID;
		
	}
	

	/**
	 * Handles the join() syscall.
	 */
	private int handleJoin(int childID, int statusPtr) {
		UserProcess childToJoin = null;
		for (UserProcess child : children) {
			if (child.PID == childID) {
				childToJoin = child;
				break;
			}
		}
		if (childToJoin == null)
			return -1;

		// Wait for child to die to join with it.
		childToJoin.waitForDeath.P();
		byte[] status = new byte[1];
		status[0] = (byte) childToJoin.exitStatus;
		writeVirtualMemory(statusPtr,status);
		if (status[0] == 0)
			return 1;
		else
			return 0;
	}

	/**
	 * Handles the exit() syscall.
	 */
	private int handleExit(int status) {
		unloadSections();

		// Close all open files.
		for (int i = 0; i < maxOpenFiles; i++)
			handleClose(i);

		exitStatus = status;
		// Signal parent that you are dead.
		waitForDeath.V();
		numRunningProcesses--;
		if (numRunningProcesses == 0)
			Kernel.kernel.terminate();

		thisThread.finish();
		return 0;
	}

	/**
	 * Inserts the file into the files array, and returns the index of this
	 * file. If there are no more room for files, a -1 is returned instead, to
	 * inform the user that the file could not be opened.
	 */
	private int getFileId() {
		for (int i = 0; i < files.length; i++) {
			if (files[i] == null)
				return i;
		}
		// File can't be added, no more room
		return -1;
	}

	private static final int syscallHalt = 0, syscallExit = 1, syscallExec = 2,
			syscallJoin = 3, syscallCreate = 4, syscallOpen = 5,
			syscallRead = 6, syscallWrite = 7, syscallClose = 8,
			syscallUnlink = 9;

	/**
	 * Handle a syscall exception. Called by <tt>handleException()</tt>. The
	 * <i>syscall</i> argument identifies which syscall the user executed:
	 * 
	 * <table>
	 * <tr>
	 * <td>syscall#</td>
	 * <td>syscall prototype</td>
	 * </tr>
	 * <tr>
	 * <td>0</td>
	 * <td><tt>void halt();</tt></td>
	 * </tr>
	 * <tr>
	 * <td>1</td>
	 * <td><tt>void exit(int status);</tt></td>
	 * </tr>
	 * <tr>
	 * <td>2</td>
	 * <td><tt>int  exec(char *name, int argc, char **argv);
	 * 								</tt></td>
	 * </tr>
	 * <tr>
	 * <td>3</td>
	 * <td><tt>int  join(int pid, int *status);</tt></td>
	 * </tr>
	 * <tr>
	 * <td>4</td>
	 * <td><tt>int  creat(char *name);</tt></td>
	 * </tr>
	 * <tr>
	 * <td>5</td>
	 * <td><tt>int  open(char *name);</tt></td>
	 * </tr>
	 * <tr>
	 * <td>6</td>
	 * <td><tt>int  read(int fd, char *buffer, int size);
	 * 								</tt></td>
	 * </tr>
	 * <tr>
	 * <td>7</td>
	 * <td><tt>int  write(int fd, char *buffer, int size);
	 * 								</tt></td>
	 * </tr>
	 * <tr>
	 * <td>8</td>
	 * <td><tt>int  close(int fd);</tt></td>
	 * </tr>
	 * <tr>
	 * <td>9</td>
	 * <td><tt>int  unlink(char *name);</tt></td>
	 * </tr>
	 * </table>
	 * 
	 * @param syscall
	 *            the syscall number.
	 * @param a0
	 *            the first syscall argument.
	 * @param a1
	 *            the second syscall argument.
	 * @param a2
	 *            the third syscall argument.
	 * @param a3
	 *            the fourth syscall argument.
	 * @return the value to be returned to the user.
	 */
	public int handleSyscall(int syscall, int a0, int a1, int a2, int a3) {
		System.out.println("handling syscall");
		switch (syscall) {
		case syscallHalt:
			System.out.println("handling halt");
			return  handleHalt();
		case syscallCreate:
			return handleCreate(a0);
		case syscallOpen:
			return handleOpen(a0);
		case syscallClose:
			return handleClose(a0);
		case syscallRead:
			return handleRead(a0, a1, a2);
		case syscallWrite:
			return handleWrite(a0, a1, a2);
		case syscallUnlink:
			return handleUnlink(a0);
		case syscallExec:
			return handleExec(a0,a1,a2);
		case syscallJoin:
			return handleJoin(a0,a1);
		case syscallExit:
			return handleExit(a0);
		default:
			Lib.debug(dbgProcess, "Unknown syscall " + syscall);
			Lib.assertNotReached("Unknown system call!");
		}
		return 0;
	}

	/**
	 * Handle a user exception. Called by <tt>UserKernel.exceptionHandler()</tt>
	 * . The <i>cause</i> argument identifies which exception occurred; see the
	 * <tt>Processor.exceptionZZZ</tt> constants.
	 * 
	 * @param cause
	 *            the user exception that occurred.
	 */
	public void handleException(int cause) {
		Processor processor = Machine.processor();

		switch (cause) {
		case Processor.exceptionSyscall:
			int result = handleSyscall(processor.readRegister(Processor.regV0),
					processor.readRegister(Processor.regA0),
					processor.readRegister(Processor.regA1),
					processor.readRegister(Processor.regA2),
					processor.readRegister(Processor.regA3));
			processor.writeRegister(Processor.regV0, result);
			processor.advancePC();
			break;

		default:
			// Exit signaling an error occured.
			handleExit(0);

			Lib.debug(dbgProcess, "Unexpected exception: "
					+ Processor.exceptionNames[cause]);
			break;
			
			//Lib.assertNotReached("Unexpected exception");
		}
	}

	/**
	 * List of physical pages allocated to this process Set by
	 * UserKerner.requestPages(). Saved to ease release of pages.
	 */
	private int[] physicalPages;

	/** The program being run by this process. */
	protected Coff coff;

	/** This process's page table. */
	protected TranslationEntry[] pageTable;

	/** The number of contiguous pages occupied by the program. */
	protected int numPages;

	/** The number of pages in the program's stack. */
	protected final int stackPages = 8;

	private int initialPC, initialSP;
	private int argc, argv;
	
	private int PID = 0;
	private UThread thisThread;

	// used to store open files
	private OpenFile[] files;
	private int[] filePositions;
	private Vector<UserProcess> children;
	private int exitStatus;
	private Semaphore waitForDeath;

	private static Vector<String> allFiles = new Vector<String>();
	private static Vector<String> unlinkedFiles = new Vector<String>();
	private static int nextPID = 1;
	private static int numRunningProcesses = 0;

	private static final int maxOpenFiles = 16;
	private static final int fileBufferSize = 16384;
	private static final int maxStringLength = 256;
	private static final int pageSize = Processor.pageSize;
	private static final char dbgProcess = 'a';
}
